package cn.huyanbing.middleware.sentinel.arithmetic;

import java.time.LocalTime;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @BelongProject middleware
 * @BelongPackage cn.huyanbing.middleware.sentinel.arithmetic
 * @Description
 *
 * https://mp.weixin.qq.com/s/2G1Rlhic9M0zTzewb3rRfQ
 * 计数器算法，通过计数器在周期内累加访问次数，并规定一个周期(时间窗口)内的系统处理的请求数量(阈值)，当在一个周期内，计数的值达到限流阈值时触发拒绝策略；到下一周期计数器就重置为0，重新开始计数，它是一种简单方便的限流算法
 *
 * 比如我们设置系统的阈值1s中最多请求100次，在计数器算法中，我们需要把时间划分固定大小窗口(所以计数器算法又叫固定窗口算法Fixed Window)，这里我们将1s划分为一个时间窗口；然后用计数器来记录在每个时间窗口的处理的请求数量，
 * 如果请求数量超过100次，后续的请求会被直接拒绝，直到1s结束后，重新开始计数
 * @ClassName MyFixedWindowRateLimiterDemo
 * @Author huyangbing
 * @Date 2024/8/23 23:27
 * @Version 1.0.0
 */
public class MyFixedWindowRateLimiterDemo {
    // 阈值
    private static Integer QPS = 2;
    // 时间窗口（毫秒）
    private static long TIME_WINDOWS = 1000;
    // 计数器
    private static AtomicInteger counter = new AtomicInteger();

    //上一个窗口的开始时间
    private static long START_TIME = System.currentTimeMillis();

    public synchronized static boolean tryAcquire() {
        if ((System.currentTimeMillis() - START_TIME) > TIME_WINDOWS) {
            counter.set(0);
            START_TIME = System.currentTimeMillis();
        }
        return counter.incrementAndGet() <= QPS;
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread.sleep(250);
            LocalTime now = LocalTime.now();
            if (!tryAcquire()) {
                System.out.println(now + " 请求被限流");
            } else {
                System.out.println(now + " 请求通过");
            }
        }
    }
}